#include "defs.h"
#include "AbstractBoard.h"

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <queue>


AbstractBoard::AbstractBoard()
{
    w = h = 19;
    group_id = 1;

    for (int y = 0; y < h; y++)
    {
        for (int x = 0; x < w; x++)
        {
            board[y][x] = EMPTY;
            groups[y][x] = 0;
        }
    }

    move_tree = new MoveTree;
}

void AbstractBoard::printBoard()
{
    std::cout << "Board:\n";
    for (int y = 0; y < h; y++)
    {
        for (int x = 0; x < w; x++)
        {
            if (board[y][x] == EMPTY)
                std::cout << ". ";
            else if (board[y][x] == BLACK)
                std::cout << "x ";
            else if (board[y][x] == WHITE)
                std::cout << "o ";
        }
        std::cout << std::endl;
    }

    printf("\nGroups:\n");
    for (int y = 0; y < h; y++)
    {
        for (int x = 0; x < w; x++)
        {
            if (groups[y][x] == 0)
                printf(" . ");
            else
                printf("%2d ", groups[y][x]);
        }
        printf("\n");
    }
}

bool AbstractBoard::isEmptySpot(int x, int y)
{
    if (board[y][x] == EMPTY)
        return true;
    return false;
}

int AbstractBoard::getPlay(int x, int y)
{
    return board[y][x];
}

void AbstractBoard::makeBlackPlay(int x, int y)
{
    board[y][x] = BLACK;

    addToGroups(x, y, BLACK);
    processEnemyCapture(x, y);

    printBoard();
}

void AbstractBoard::makeWhitePlay(int x, int y)
{
    board[y][x] = WHITE;

    addToGroups(x, y, WHITE);
    processEnemyCapture(x, y);

    printBoard();
}

bool AbstractBoard::makePlay(int x, int y, int color)
{
    printf("\n\n============================================\n\nNew Turn\n\n============================================\n\n");
    if (color == BLACK)
        makeBlackPlay(x, y);
    else
        makeWhitePlay(x, y);

    move_tree->addMove(x, y, color);

    if (suicide(x, y))
    {
        undo();
        return false;
    }

    return true;
}


void AbstractBoard::clear()
{
    group_id = 1;

    for (int y = 0; y < h; y++)
    {
        for (int x = 0; x < w; x++)
        {
            board[y][x] = EMPTY;
            groups[y][x] = 0;
        }
    }
}

int AbstractBoard::undo()
{
    clear();

    std::vector<MoveTreeCell *> *move_list = move_tree->getMoveList();

    move_tree->clear();

    int size = move_list->size();
    for (int i = 0; i < size-1; i++)
    {
        MoveTreeCell *c = move_list->at(i);
        makePlay(c->x, c->y, c->color);
    }

    return ((size-1)%2 + 1);
}

void AbstractBoard::addToGroups(int x, int y, int color)
{
    groups[y][x] = group_id;
    group_id++;

    std::vector<Point *> neighbors = getSameColorNeighbors(x, y, color);

    printf("Neighbors: ");
    printPointList(neighbors);
    printf("\n");

    if (neighbors.size() > 0)
        unite(x, y, neighbors);
}

std::vector<Point *> AbstractBoard::getSameColorNeighbors(int x, int y, int color)
{
    std::vector<Point *> neighbors;

    if (x > 0 && board[y][x-1] == color)
        neighbors.push_back(new Point(x-1, y));
    if (x < w-1 && board[y][x+1] == color)
        neighbors.push_back(new Point(x+1, y));
    if (y > 0 && board[y-1][x] == color)
        neighbors.push_back(new Point(x, y-1));
    if (y < h-1 && board[y+1][x] == color)
        neighbors.push_back(new Point(x, y+1));

    return neighbors;
}

void AbstractBoard::unite(int x, int y, std::vector<Point *> neighbors)
{
    std::vector<Point *>::iterator it;
    for (it = neighbors.begin(); it != neighbors.end(); it++)
    {
        std::vector<Point *> group = getGroup((*it)->x, (*it)->y);
        std::vector<Point *>::iterator group_it;
        for (group_it = group.begin(); group_it != group.end(); group_it++)
        {
            int elem_x = (*group_it)->x;
            int elem_y = (*group_it)->y;
            groups[elem_y][elem_x] = groups[y][x];
        }
    }
}

std::vector<Point *> AbstractBoard::getGroup(int x, int y)
{
    return getGroup(groups[y][x]);
}

std::vector<Point *> AbstractBoard::getGroup(int gid)
{
    std::vector<Point *> group;
    for (int i = 0; i < h; i++)
    {
        for (int j = 0; j < w; j++)
        {
            if (groups[i][j] == gid)
                group.push_back(new Point(j, i));
        }
    }

    return group;
}

void AbstractBoard::printPointList(std::vector<Point *> list)
{
    std::vector<Point *>::iterator it;
    for (it = list.begin(); it != list.end(); it++)
        std::cout << "(" << (*it)->x << ", " << (*it)->y << ") ";
}

void AbstractBoard::printIntList(std::vector<int> list)
{
    std::vector<int>::iterator it;
    for (it = list.begin(); it != list.end(); it++)
        printf("%d ", (*it));
}

void AbstractBoard::processEnemyCapture(int x, int y)
{
    std::vector<int> opponent_neighbors = getNeighborGroups(x, y);

    printf("Opponent Groups:\n");
    printIntList(opponent_neighbors);
    printf("\n");

    std::vector<int>::iterator it;
    for (it = opponent_neighbors.begin(); it != opponent_neighbors.end(); it++)
    {
        if (groupLiberties(*it) == 0)
            deleteGroup(*it);
    }
}

std::vector<int> AbstractBoard::getNeighborGroups(int x, int y)
{
    std::vector<int> neighbor_groups;

    std::vector<Point *> group = getGroup(x, y);
    std::vector<Point *>::iterator it;
    for (it = group.begin(); it != group.end(); it++)
    {
        std::vector<Point *> neighbors = getSameColorNeighbors((*it)->x, (*it)->y, flipColor(board[y][x]));
        std::vector<Point *>::iterator neighbor_it;
        for (neighbor_it = neighbors.begin(); neighbor_it != neighbors.end(); neighbor_it++)
        {
            int gid = groups[(*neighbor_it)->y][(*neighbor_it)->x];

            if (!intInList(gid, neighbor_groups))
                neighbor_groups.push_back(gid);
        }
    }

    return neighbor_groups;
}

bool AbstractBoard::pointInList(Point *p, std::vector<Point *> list)
{
    std::vector<Point *>::iterator it;
    for (it = list.begin(); it != list.end(); it++)
    {
        if ((*it)->equals(*p))
            return true;
    }

    return false;
}

bool AbstractBoard::intInList(int i, std::vector<int> list)
{
    std::vector<int>::iterator it;
    for (it = list.begin(); it != list.end(); it++)
    {
        if ((*it) == i)
            return true;
    }

    return false;
}

int AbstractBoard::flipColor(int color)
{
    if (color == BLACK)
        return WHITE;
    else if (color == WHITE)
        return BLACK;
    return EMPTY;
}

int AbstractBoard::groupLiberties(int group)
{
    std::vector<Point *> stones = getGroup(group);
    
    int count = 0;
    std::vector<Point *>::iterator it;
    for (it = stones.begin(); it != stones.end(); it++)
    {
        count += getStoneLiberties((*it)->x, (*it)->y);
    }

    return count;
}

int AbstractBoard::getStoneLiberties(int x, int y)
{
    std::vector<Point *> neighbors = getSameColorNeighbors(x, y, EMPTY);
    return neighbors.size();
}

void AbstractBoard::deleteGroup(int gid)
{
    for (int y = 0; y < h; y++)
    {
        for (int x = 0; x < w; x++)
        {
            if (groups[y][x] == gid)
            {
                groups[y][x] = 0;
                board[y][x] = EMPTY;
            }
        }
    }
}

bool AbstractBoard::suicide(int x, int y)
{
    if (groupLiberties(groups[y][x]) == 0)
        return true;
    return false;
}
